﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Jayden.Dll.Core
{
	public struct Date : IComparable<Date>
	{
		public static readonly Date Unknown = new Date(null, null, null);

		private int? m_Day;
		private int? m_Month;
		private int? m_Year;

		public int? Day { get { return m_Day; } set { m_Day = value; Validate(); } }
		public int? Month { get { return m_Month; } set { m_Month = value; Validate(); } }
		public int? Year { get { return m_Year; } set { m_Year = value; Validate(); } }
		public Date(DateTime dateTime)
		{
			m_Day = dateTime.Day;
			m_Month = dateTime.Month;
			m_Year = dateTime.Year;
		}
		public Date(int? year, int? month, int? day)
		{
			m_Day = day;
			m_Month = month;
			m_Year = year;
		}

		public bool HasDay { get { return Day.HasValue; } }
		public bool HasMonth { get { return Month.HasValue; } }
		public bool HasYear { get { return Year.HasValue; } }

		public bool HasAll { get { return HasDay && HasMonth && HasYear; } }

		private void Validate()
		{
			if (HasDay)
				if (Day.Value < 1 || Day.Value > 31)
					throw new Exception("Invalid day value.");
			if (HasMonth)
				if (Month.Value < 1 || Month.Value > 12)
					throw new Exception("Invalid month value.");
			if (HasDay && HasMonth)
			{
				if (Month.Value == 2 && Day.Value > 29)
					throw new Exception("Invalid day value for month.");
				if (DateTime.DaysInMonth(DateTime.Today.Year, Month.Value) < Month.Value)
					throw new Exception("Invalid day value for month.");
			}
			if (HasAll)
			{
				DateTime dateTime = new DateTime(Year.Value, Month.Value, Day.Value);
				if (dateTime.Year != Year.Value || dateTime.Month != Month.Value || dateTime.Day != Day.Value)
					throw new Exception("Invalid date.");
			}
		}

		public int CompareTo(Date other)
		{
			int compare = 0;
			if (HasYear && other.HasYear)
				compare = Year.Value.CompareTo(other.Year.Value);
			else if (HasYear || other.HasYear)
				return HasYear ? 1 : -1;
			if (compare != 0)
				return compare;
			if (HasMonth && other.HasMonth)
				compare = Month.Value.CompareTo(other.Month.Value);
			else if (HasMonth || other.HasMonth)
				return HasMonth ? 1 : -1;
			if (compare != 0)
				return compare;
			if (HasDay && other.HasDay)
				compare = Day.Value.CompareTo(other.Day.Value);
			else if (HasDay || other.HasDay)
				return HasDay ? 1 : -1;
			return compare;

		}

		public override string ToString()
		{
			string year = HasYear ? Year.Value.ToString().PadLeft(4, '0') : "????";
			string month = HasMonth ? Month.Value.ToString().PadLeft(2, '0') : "??";
			string day = HasDay ? Day.Value.ToString().PadLeft(2, '0') : "??";
			return string.Format("{0}/{1}/{2}", year, month, day);
		}
	}
}
